#ifndef SERVER_SYNC_AGENT_H
#define SERVER_SYNC_AGENT_H

#include "Rpc.h"
#include <sqlite3.h>
#include <string>
#include <gtest/gtest.h>

class ServerSyncAgent
  {
  public:
    ~ServerSyncAgent (void);
    ServerSyncAgent ();

    /**
     * Create a sync agent
     *
     * \param bankDbName filename of the local bank database file
     * \param backupSvc host name of the backup server
     */
    ServerSyncAgent (std::string bankDbName, std::string backupSvc);    
    /**
     * Open a connection with the bank database, and a RPC communication 
     * connection with remote backup server
     *
     * \param bankDbName filename of the local bank database file
     * \param backupSvc host name of the backup server
     */
    void Open (std::string bankDbName, std::string backupSvc = "");
    /**
     * Close the database connection and the RPC communication connection
     */
    void Close ();

    /**
     * Record the new balance and prepare for sync
     *
     * \param cusId1 the ID of a customer
     * \param balance1 the new balance of the customer
     * \param updateCount the new updateCount value
     * \param cusId2 (optional) the ID of another customer
     * \param balance2 (optional) the new balance of the other customer
     */
    void Record (std::string cusId1, double balance1, int updateCount, std::string cusId2 = "", double balance2 = 0.0);
    /**
     * Performance database sync from local to the remote backup server
     */
    void Sync ();
    /**
     * Notify the remote backup server to do a ::Sync () from remote to local
     */
    void SyncToMe ();
    /**
     * Set the reocrd with a given updateCount to be "SyncFinished" in the SyncAgentTable
     *
     * \param updateCount indicate which record is now "SyncFinished"
     */
    void SetSyncFinished (int updateCount);

  private:
    bool CreateClient ();
    bool RecreateClient ();
    /// return false if RPC call is failed; true otherwise
    template <typename RET, typename ARGS> bool RpcCallingImpl (
      ARGS* args, 
      RET* (*RpcFunc)(ARGS*,CLIENT*), 
      std::string RpcFuncName, 
      int* serverUpdateCount = NULL);

    /// the hostname of the backup server
    std::string m_backupSvc;
    /// a client handle for accessing backup RPC server
    CLIENT* m_clnt;
    /// a SQLite3 database handle for accessing the "SyncAgentTable"
    sqlite3* m_db;
    /// a SQLite3 prepared statement for recording one customer balance
    sqlite3_stmt* m_record1CusStmt;
    /// a SQLite3 prepared statement for recording two customer balances
    sqlite3_stmt* m_record2CusStmt;
    /// a SQLite3 prepared statement for selecting the pending BankTable update
    sqlite3_stmt* m_selectStmt;
    /// a SQList3 prepared statement for setting the "SyncFinished" bit of a row to true
    sqlite3_stmt* m_setSyncFinishedTrueStmt;
    /// true if the database connection is created
    bool m_dbIsOpened;

    FRIEND_TEST (ServerSyncAgentMethodTest, RecreateClientTest);
  };

extern ServerSyncAgent g_syncAgent;

#endif /* ifndef SERVER_SYNC_AGENT_H */
